package de.mmis.core.tuplespace.gui;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.Scrollable;

import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.abstracttree.TreeParserException;
import de.mmis.core.base.device.Device;
import de.mmis.core.base.event.Observable;
import de.mmis.core.base.event.Observer;
import de.mmis.core.sexpression.SExpressionParser;
import de.mmis.core.tuplespace.Tuplespace;
import de.mmis.core.tuplespace.TuplespaceException;
import de.mmis.core.tuplespace.gui.TupleListEditor.TupleList;
import de.mmis.core.tuplespace.gui.TuplespacePanel.TuplespacePanelEvent;

/**
 * A {@link JPanel} containing all elements of the TuplespaceBrowser
 * 
 * @author Martin Nyolt
 * 
 */
public class TuplespaceBrowserPanel extends JPanel implements Scrollable {

	private static final long serialVersionUID = 1L;

	private TuplespacePanel panel_tuple;
	private JScrollPane scroll_tuple;
	private JTextField commandPattern;
	private JButton startScanButton;

	private JTextField selectedTuple;
	private JButton writeButton;
	private JButton deleteButton;
	private JLabel label_numberOfTuples;

	private JComboBox combo_tupleLists;
	private JButton button_manageList;
	private JButton button_newList;

	private HashMap<String, TupleList> tupleLists = new HashMap<String, TupleList>();
	private HashMap<String, TupleListEditor> tupleListEditors = new HashMap<String, TupleListEditor>();
	private Device<Tuplespace> space;

	public TuplespaceBrowserPanel(Device<Tuplespace> space)
			throws TuplespaceException {
		this.space = space;
		initGUI();
	}

	private void initGUI() throws TuplespaceException {
		GridBagLayout layout = new GridBagLayout();
		layout.rowWeights = new double[] { 0.0, 1.0, 0.0, 0.0, 0.0 };
		layout.rowHeights = new int[] { 1, 75, 1, 1, 1 };
		layout.columnWeights = new double[] { 1.0, 0.0, 0.0 };
		layout.columnWidths = new int[] { 50, 1, 1 };
		setLayout(layout);

		Insets insets = new Insets(5, 5, 5, 5);

		{
			commandPattern = new javax.swing.JTextField();
			commandPattern
					.addActionListener(new java.awt.event.ActionListener() {
						@Override
						public void actionPerformed(
								java.awt.event.ActionEvent evt) {
							updatePattern();
						}
					});
			add(commandPattern, new GridBagConstraints(0, 0, 2, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));

			startScanButton = new javax.swing.JButton();
			startScanButton.setText("Scan Space");
			startScanButton
					.addActionListener(new java.awt.event.ActionListener() {
						@Override
						public void actionPerformed(
								java.awt.event.ActionEvent evt) {
							updatePattern();
						}
					});

			add(startScanButton, new GridBagConstraints(2, 0, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));
		}

		{
			panel_tuple = new TuplespacePanel(space);
			panel_tuple.setRequestFocusEnabled(false);
			panel_tuple.addObserver(new Observer<TuplespacePanelEvent>() {
				@Override
				public void notify(
						Observable<? extends TuplespacePanelEvent> sender,
						TuplespacePanelEvent event) {
					switch (event.getEventType()) {
					case TUPLE_SELECTED:
						tupleSelected(event.expression);
						break;
					case SPACE_CHANGED:
						updateNumberOfTuplesCount(panel_tuple.getTupleCount());
					}
				}
			});
			scroll_tuple = new JScrollPane(panel_tuple);

			add(scroll_tuple, new GridBagConstraints(0, 1, 3, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.BOTH, insets,
					0, 0));
		}

		{
			selectedTuple = new javax.swing.JTextField();
			add(selectedTuple, new GridBagConstraints(0, 2, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));

			writeButton = new JButton("Write");
			writeButton.addActionListener(new java.awt.event.ActionListener() {
				@Override
				public void actionPerformed(java.awt.event.ActionEvent evt) {
					writeButtonActionPerformed(evt);
				}
			});
			add(writeButton, new GridBagConstraints(1, 2, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));

			deleteButton = new JButton("Delete");
			deleteButton.addActionListener(new java.awt.event.ActionListener() {
				@Override
				public void actionPerformed(java.awt.event.ActionEvent evt) {
					deleteButtonActionPerformed(evt);
				}
			});
			add(deleteButton, new GridBagConstraints(2, 2, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));
		}

		{
			label_numberOfTuples = new JLabel();
			updateNumberOfTuplesCount(0);
			add(label_numberOfTuples, new GridBagConstraints(0, 3, 3, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));
		}

		{
			combo_tupleLists = new JComboBox();
			add(combo_tupleLists, new GridBagConstraints(0, 4, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));

			button_manageList = new JButton("Manage");
			button_manageList.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					String name = (String) combo_tupleLists.getSelectedItem();
					if (name == null)
						return;

					TupleListEditor editor = tupleListEditors.get(name);
					if (editor == null) {
						TupleList list = tupleLists.get(name);
						editor = new TupleListEditor(name, list, space);
						tupleListEditors.put(name, editor);
					}
					editor.setVisible(true);
					editor.toFront();
				}
			});
			add(button_manageList, new GridBagConstraints(1, 4, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));

			button_newList = new JButton("New");
			button_newList.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					String name = JOptionPane
							.showInputDialog("Please enter the name of the new TupleList");
					if (name == null)
						return;

					tupleLists.put(name, new TupleList());
					combo_tupleLists.addItem(name);
				}
			});
			add(button_newList, new GridBagConstraints(2, 4, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));
		}
	}

	private void updateNumberOfTuplesCount(int count) {
		label_numberOfTuples.setText("Found " + count + " tuples");
	}

	/**
	 * creates new notification
	 */
	private void updatePattern() {
		// set notification-template to new pattern

		try {
			if (commandPattern.getText().length() == 0)
				panel_tuple.setPattern(null);
			else {
				Tree pattern = SExpressionParser.parseUTF8(commandPattern
						.getText());
				panel_tuple.setPattern(pattern);
			}
			updateNumberOfTuplesCount(panel_tuple.getTupleCount());
		} catch (TuplespaceException e) {
			JOptionPane.showMessageDialog(this,
					new Object[] { "TupleSpaceClientException",
							new JScrollPane(new JList(e.getStackTrace())) },
					"Error", JOptionPane.ERROR_MESSAGE);
		} catch (TreeParserException e) {
			JOptionPane.showMessageDialog(this,
					new Object[] { "SExpressionParserException",
							new JScrollPane(new JList(e.getStackTrace())) },
					"Error", JOptionPane.ERROR_MESSAGE);
		}
	}

	/**
	 * writes tuple from "selected tuple"-field to tuplespace
	 */
	private void writeButtonActionPerformed(ActionEvent evt) {
		if (space == null)
			return;

		space.ensureAvailability();
		try {
			space.getDevice().write(
					SExpressionParser.parseUTF8(selectedTuple.getText()));
			// error-dialog showing detailed information (stacktrace)
		} catch (TuplespaceException e) {
			JOptionPane.showMessageDialog(this,
					new Object[] { "TupleSpaceClientException",
							new JScrollPane(new JList(e.getStackTrace())) },
					"Error", JOptionPane.ERROR_MESSAGE);
			// error-dialog showing detailed information (stacktrace)
		} catch (TreeParserException e) {
			JOptionPane.showMessageDialog(this,
					new Object[] { "SExpressionParserException",
							new JScrollPane(new JList(e.getStackTrace())) },
					"Error", JOptionPane.ERROR_MESSAGE);
		} finally {
			space.releaseAvailability();
		}
	}

	// takes tuple from "selected tuple"-field from tuplespace
	private void deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {
		if (space == null)
			return;

		space.ensureAvailability();
		try {
			space.getDevice().takeIfExists(
					SExpressionParser.parseUTF8(selectedTuple.getText()));
			// error-dialog showing detailed information (stacktrace)
		} catch (TuplespaceException e) {
			Component JPane = null;
			JOptionPane.showMessageDialog(JPane,
					new Object[] { "TupleSpaceClientException",
							new JScrollPane(new JList(e.getStackTrace())) },
					"Error", JOptionPane.ERROR_MESSAGE);
			// error-dialog showing detailed information (stacktrace)
		} catch (TreeParserException e) {
			Component JPane = null;
			JOptionPane.showMessageDialog(JPane,
					new Object[] { "SExpressionParserException",
							new JScrollPane(new JList(e.getStackTrace())) },
					"Error", JOptionPane.ERROR_MESSAGE);
		} finally {
			space.releaseAvailability();
		}
	}

	// sets "selected tuple"-field to tuple selected from list
	private void tupleSelected(Tree exp) {
		selectedTuple.setText(exp.toString());
	}

	public void setTuplespace(Device<Tuplespace> space)
			throws TuplespaceException {
		this.space = space;
		panel_tuple.setTuplespace(space);
	}

	@Override
	public Dimension getPreferredScrollableViewportSize() {
		return getPreferredSize();
	}

	@Override
	public int getScrollableUnitIncrement(Rectangle visibleRect,
			int orientation, int direction) {
		return 10;
	}

	@Override
	public int getScrollableBlockIncrement(Rectangle visibleRect,
			int orientation, int direction) {
		return 50;
	}

	@Override
	public boolean getScrollableTracksViewportWidth() {
		// disable scrolling
		return true;
	}

	@Override
	public boolean getScrollableTracksViewportHeight() {
		// disable scrolling
		return true;
	}
}
